「フロントエンド技術の波を乗り越える!Vue2からReactへの移行とアーキテクチャ設計による堅牢化」というタイトルで登壇しました #cm_odyssey

「フロントエンド技術の波を乗り越える!Vue2からReactへの移行とアーキテクチャ設計による堅牢化」というタイトルで登壇しました #cm_odyssey

クラスメソッド設立20周年イベントとして開催されたClassmethod Odyssey ONLINE(クラウド)にて2024/7/30に登壇した資料を公開します。

はじめに

リテールアプリ共創部の中野です。

クラスメソッド設立20周年イベントとして開催されたClassmethod Odysseyのオンラインイベントにて2024/7/30に登壇しました。
「フロントエンド技術の波を乗り越える!Vue2からReactへの移行とアーキテクチャ設計による堅牢化」というタイトルでお話したセッション資料を公開します。

https://classmethod.connpass.com/event/322711/

セッション概要

フロントエンド技術の変化に翻弄されることなく、ソフトウェアのコアを守り抜くことが重要です。
今回は、Vue2からVue3への移行検討で課題に直面した事例を紹介します。大規模な書き換えが必要だったため、Reactへの移行とフロントエンドコードのクリーンアーキテクチャによる再設計を行い、コアロジックを守りぬくことができました。フロントエンド技術の変化に左右されないソフトウェア設計の方法を解説します。

登壇資料

登壇内容

登壇資料の内容のポイント部分を、一部掲載します。
このブログ内の内容以外にも詳細を確認されたい場合は、登壇スライドをご参照ください。

目次

  • Vue2からReactへリライトを意思決定した理由
  • リライトヘ当たって直面した課題
  • 課題を解決するための取り組み
  • フロントエンドクリーンアーキテクチャ導入解説
  • リライト後の変化
  • 学んだ教訓

Vue2からReactへ移行を意思決定した理由

理由1: Vue2のEOL

Vue2は2023年12月末に EOL (End of Life) を迎えていました。
https://v2.vuejs.org/eol/

正規ルートだと「Vue2からVue3」、現トレンドだと「Vue2からReact」にするか、世間では大きく2つのルートに分かれていました。

Vue 2 の最新リリースである 2.7.16 は、Vue 2 の最終リリースであり、2.7 機能の最終的な修正が含まれており、さらに、Vue 3 との型の整合性がありました。

理由2: Vue2からVue3の移行のコスト

Options APIを利用している場合、Composition APIへの置き換えが必要でした。
Vue2だと、各コンポーネントのthisに状態を変更するロジックが依存するため、関心事の単位で関数をまとめることが難しく、Options APIを利用するコード周辺に分散しがちで「状態を持つ関数」の再利用がしにくいという問題がありました。
Vue2に関連付属するライブラリの置き換えが必要でした。

理由3: React主流による保守体制確保の容易性

Reactは現行のトレンドであり、Vueよりも主流になっています。
今後の追加機能開発を進める上で、Reactの方が開発工数が少なくて済むと判断しました。

理由4: チームの技術スタックをそろえることによる効用

私たちのチームは、技術スタックを可能な限り統一する方針をとっており、技術への認知負荷を低減し、顧客への価値提供速度を上げることができました。

理由5: メンバー変更によるナレッジ不足の補強

ソフトウェアプロダクトにおいて担当者の交代は「あるある」です。
担当者が変わっても継続してユーザーへ価値提供を促進できるよう、情報を理解しやすい形にとどめておくことが必要でした。

理由6: 今後のビジネス価値への寄与に期待できた

リライトすることによるステークホルダーの見返りを考えました。

例: ビッグリライトする代わりに新機能を追加で入れる、セキュリティ面が補強される最新のアーキテクチャにできる、キャッシュ機構を導入しレスポンスが高速化し品質向上

アーキテクチャ概要

クリーンアーキテクチャとは?

クリーンアーキテクチャは、システムの関心事を分離するためにソフトウェアをレイヤーに分割する設計手法です
Robert C. Martin氏が提唱しました(公開ブログ:2012年8月、書籍:2017年)

クリーンアーキテクチャで設計されたシステムの特徴

  • フレームワークに依存しない
    • 特定のフレームワークやライブラリに依存しません
    • フレームワークやライブラリをツールとして使用でき、システムをその制約にべったり合わせることは不要です
  • テスタビリティ(テスト容易性)向上
    • ビジネスルールはUI、データベース、Webサーバーなどの外部要素なしでテスト可能です
  • UIに依存しない
    • UIは簡単に変更可能で、ビジネスルールに影響を与えません
  • 特定のデータベースに依存しない
    • OracleやPostgraSQL、MongoDBなどのDBが変わってもアプリケーションの機能を維持できます
  • 外部API、外部システムに依存しない
    • ビジネスルールは外部の世界について何も知る必要がありません

The Clean Code Blog - The Clean Architecture - を参考にしました

サンプルコード

  • アルバムと写真を表示する簡易アプリ
  • Reactベースでフロントエンドをクリーンアーキテクチャで設計

https://github.com/drumnistnakano/react-clean-architecture-sample

アプリの実行

  • Webサーバー起動の場合

開発用のWebサーバーを起動するには、以下のコマンドを実行します。

npm run serve -w photo-album

これにより、Viteでサーバーが起動し、http://localhost:3000 へアクセスするとアプリを閲覧できます。

  • CLI起動の場合

データを取得するためのCLIを実行するには、以下のコマンドを実行します。

npm run cli -w photo-album

実行後、すべてのアルバムを取得するか、アルバムIDごとに写真を取得するかなどのタスクを選択するように求められます。

例:

npm run cli -w photo-album          

> cli
> npm run start ./src/framework/cli/main.ts

> start
> npx tsx ./src/framework/cli/main.ts

? CLIで実行するタスクを選択してください すべてのアルバムを取得する
? 実行しますか?controller=すべてのアルバムを取得する yes
{
  "message": "全てのアルバムデータを取得します",
  "logLevel": "INFO"
}
{
  "message": "use-case: find-album-use-case-impl",
  "logLevel": "DEBUG"
}
{
  "message": "アルバムデータの取得に成功しました",
  "data": [
    {
      "id": 1,
      "title": "quidem molestiae enim"
    },

--- 以下、省略 ---
npm run cli -w photo-album

> cli
> npm run start ./src/framework/cli/main.ts

> start
> npx tsx ./src/framework/cli/main.ts

? CLIで実行するタスクを選択してください アルバムIDで写真を取得する
? 実行しますか?controller=アルバムIDで写真を取得する yes
? アルバムIDを入力してください 50
{
  "message": "アルバムID 50 の写真データを取得します",
  "logLevel": "INFO"
}
{
  "message": "use-case: find-photo-use-case-impl",
  "logLevel": "DEBUG"
}
{
  "message": "写真データの取得に成功しました",
  "data": {
    "photos": [
      {
        "id": 2451,
        "title": "odio animi nobis cumque",
        "url": "https://via.placeholder.com/600/8fef3c",
        "thumbnailUrl": "https://via.placeholder.com/150/8fef3c"
      },

--- 以下、省略 ---

ディレクトリ構造

以下は、クリーンアーキテクチャに基づいて設計されたプロジェクトのディレクトリ構造です。この構造により、Coreの部分がタマネギ(クリーンアーキテクチャの同心円)の内側、Frameworkの部分がタマネギの一番外側に配置させるようにして、各層の責務が明確にしました。
また、Webブラウザから表示するだけではなく、CLIからもデータを取得できるようにしています。

.
└── src
    ├── core // コアロジック
    │   ├── domain // ドメイン層
    │   │   ├── entities   // エンティティ定義
    │   │   ├── repository // リポジトリのインターフェース定義
    │   │   └── support    // API Clientなどのサポート関数の定義
    │   ├── infrastructure // インフラ層のインターフェース格納先
    │   │   ├── api-client // API Clientの実装詳細
    │   │   ├── logger     // Loggerの実装詳細
    │   │   └── repository // リポジトリの実装詳細
    │   ├── usecase // アプリケーションユースケース層
    │   └── util    // Coreで利用可能なユーティリティ関数
    ├── di-container // 依存注入周り
    │   ├── env-util.ts
    │   ├── register-container.ts
    │   └── service-id.ts
    └── framework // フレームワーク層
        ├── cli // CLI
        │   ├── controllers // Usecase層への橋渡し
        │   └── main.ts // CLIのエントリポイント
        └── web // Web
            ├── presenters // Usecase層への橋渡し
            ├── EntryPoint.tsx // Webのエントリポイント
            ├── components // Reactのコンポーネント群
            ├── pages // React Routerのページ配置
            └── util // Web用ユーティリティ関数

レイヤー間の関係図

以下の図は、各レイヤー間の関係を示しています。各レイヤーは独立しており、依存関係は内側のレイヤーに向かって一方向にのみ存在します。

clean-architecture-chart

レイヤー毎の定義

説明
Domain層 ドメインロジックを定義する層。ビジネスルールやエンティティを含む
Infrastructure層 外部システムとのやり取りを担当。APIクライアントやデータベースアクセスを含む
UseCase層 ユースケース。ドメイン層とインフラ層をつなぐ
Framework層 ユーザーインターフェースを担当。Reactコンポーネントを含む

まとめ

以下、登壇資料のまとめです。

  • フレームワークやライブラリをつかうと楽に早く開発できる
  • フレームワークやライブラリのクラスやメソッドを使い込んで思いっきり結合してくれることを提供側は望んでいるし、利用者も同様
  • しかし、フレームワーク依存しすぎると自分たちが意図しない方向(フロントエンドのトレンド、EOL、破壊的変更など)で書き換えやリファクタリングが必要になってくる場合がある

チームでライブラリやフレームワーク自体を統一する標準化をとるのもいいが、ソフトウェアアーキテクチャを統一して解決する考え方もある

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.